package PlugIn.loader;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

import FilePanel.Data;
import FilePanel.FilePanelModel;
import PlugIn.PlugIn;
import PlugIn.PlugInImp;
import PlugIn.PlugInModel;

public class PlugInFactory {
	/** 
	 * Variable de Classe
	 * Implémentation du patron Singleton
	 */
	private static PlugInFactory factory;


	public String dir;
	public Data model;
	public ArrayList<String> names;
	public PlugInLoader loader;
	public HashMap<String,Class<?>> classes;
	public HashMap<String,PlugIn> plugins;


	public static PlugInFactory getFactory() {
		return PlugInFactory.factory;
	}

	private PlugInFactory(String dir,Data model) throws MalformedURLException {
		super();
		if(!dir.endsWith(File.separator)){
			dir+=File.separator;
		}
		this.dir = dir;
		this.model = model;
		names = new ArrayList<String>();
		loader = new PlugInLoader(new URL("file:"+dir));
		classes = new HashMap<String, Class<?>>();
		plugins = new HashMap<String, PlugIn>();
	}

	private String getRelativePath(String path){
		return path.substring(dir.length());
	}

	private String extractName(File f){
		String name = f.getName();
		String extension = ".class";
		return name.substring(0,name.length()-extension.length());
	}

	public static void initialiserFactory(String dir,Data data) throws MalformedURLException{
		PlugInFactory.factory = new PlugInFactory(dir,data);
		PlugInFactory.factory.initialiser();
	}

	public ArrayList<String> getNames() {
		return names;
	}

	public PlugIn getPlugin(String name) {
		return plugins.get(name);
	}

	public PlugInLoader getPlugInLoader() {
		return loader;
	}

	public PlugIn[] getAllPlugIns() {
		int nbPlugin = names.size();
		PlugIn[] plugins = new PlugIn[nbPlugin];
		for(int i = 0 ; i<nbPlugin ; i++){
			plugins[i] = this.plugins.get(names.get(i));
		}
		return plugins;
	}

	private void initialiser(){
		File root = new File(dir);
		checkForClass(root);	
	}

	private void checkForClass(File dir){
		//System.out.println("checking dir : "+dir);
		File[] dirs = dir.listFiles(new DirectoryFilter());
		
		try{
			for(File f : dirs){
				checkForClass(f);
			}
		}catch(NullPointerException npe){}
		File[] classFiles = dir.listFiles(new ClassFilter());
		try{
			for(File f : classFiles){
				processClassFile(f);
			}
		}catch(NullPointerException npe){}
	}

	private void processClassFile(File classFile){
		String className = processPath(classFile);
		try {
			Class<?> command = loadClass(className);
			PlugIn p = instantiate(command);
			String name = extractName(classFile);
			names.add(name);
			classes.put(name, command);
			plugins.put(name, p);
		} catch (ClassNotFoundException e) {
			System.out.println("Class Not Found");
		} catch (SecurityException e) {
			System.out.println("Security");
		} catch (IllegalArgumentException e) {
			System.out.println("Wrong Argument");
		} catch (NoSuchMethodException e) {
			System.out.println("No Method Found");
		} catch (InstantiationException e) {
			System.out.println("Instantiation Exception");
		} catch (IllegalAccessException e) {
			System.out.println("Illegal Access");
		} catch (InvocationTargetException e) {
			System.out.println("Invocation");
		}
	}

	//essaie de Charger la Class dont le nom est en parametre
	private Class<?> loadClass(String className) throws ClassNotFoundException{
		return loader.loadClass(className);
	}


	//Retourne un nom de classe repsectant la hierarchie des packages depuis la racine 
	// exemple : PlugIn/commands/Size.class retourne PlugIn.commands.Size
	private String processPath(File classFile){
		String relpath = getRelativePath(classFile.getPath());
		relpath = relpath.substring(0, relpath.length()-5);
		if(File.separator.equals("\\"))
			relpath = relpath.replaceAll("\\\\","." );
		else
			relpath = relpath.replaceAll(File.separator,".");
		relpath = relpath.substring(0, relpath.length()-1);
		return relpath;
	}

	private PlugIn instantiate(Class<?> command) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException{
		Constructor<?> construct = command.getConstructor(Data.class);
		PlugInModel model = (PlugInModel)construct.newInstance(getData());
		PlugIn retour = new PlugInImp(model);
		return retour;
	}

	private Data getData() {
		return model;
	}

	public static void main (String[] args) throws MalformedURLException{
		
		
	}

}